//
// Created by zsj on 2019/12/22.
//

#ifndef SCLI_DGST_H
#define SCLI_DGST_H

#include <openssl/sha.h>
#include <openssl/dsa.h>
#include <openssl/ossl_typ.h>
#include <openssl/bio.h>
#include <openssl/pem.h>
#include <openssl/ecdsa.h>
#include "crypto_algorithm.h"
#include "rsa_file_op.h"

class dgst : public crypto_algorithm {
public:
    virtual const string encrypt() {
        string _digest = sha256(plain);

        BIO *bio_key = BIO_new_mem_buf((const void*) _pri.c_str(), -1);
        DSA *dsa = DSA_new();
        PEM_read_bio_DSAPrivateKey(bio_key, &dsa, NULL, NULL);

        unsigned char _sign[512] = {0};
        unsigned int _len = 0;
        int r = DSA_sign(0, (const unsigned char*) _digest.c_str(), SHA256_DIGEST_LENGTH, _sign, &_len, dsa);

        if (r) {
            // 加密源文件
            string _cipher = asym_encrypt(_pub, plain);

            // 编码签名长度，采用2位16进制
            cipher.clear();
            cipher += (char) _len;
            for (int i = 0; i < _len; i++) {
                cipher += _sign[i];
            }

            cipher += _cipher;

        } else {
            BIO_free(bio_key);
            DSA_free(dsa);
            __err("数字签名失败。");
        }

        BIO_free(bio_key);
        DSA_free(dsa);
        return cipher;
    }

    virtual const string decrypt() {
        BIO *bio_key = BIO_new_mem_buf(_pub.c_str(), -1);
        DSA *dsa = NULL;

        PEM_read_bio_DSA_PUBKEY(bio_key, &dsa, NULL, NULL);

        // 取出签名长度
        unsigned int _len = (unsigned int) cipher[0];

        // 取出签名
        string _sign = cipher.substr(1, _len);

        // 恢复原密文
        plain = asym_decrypt(_pri, cipher.substr(_len + 1));
        string _digest = sha256(plain);

        int r = DSA_verify(0, (const unsigned char*) _digest.c_str(), SHA256_DIGEST_LENGTH,
                           (const unsigned char*) _sign.c_str(), _len, dsa);

        BIO_free(bio_key);
        DSA_free(dsa);
        if (r <= 0) {
            plain.clear();
            __err("签名验证失败。");
        }

        return plain;
    }

    dgst() {}

    ~dgst() {}

    void set_pri(const string& pri) {
        this->_pri = pri;
    }

    void set_pub(const string& pub) {
        this->_pub = pub;
    }

private:

    string _pri;
    string _pub;

    string sha256(const string &src) {
        unsigned char mdStr[33] = {0};
        SHA256((const unsigned char *) src.c_str(), src.length(), mdStr);

        // 哈希后的十六进制串 32字节
        char buf[65] = {0};
        char tmp[3] = {0};
        for (int i = 0; i < 32; i++) {
            sprintf(tmp, "%02x", mdStr[i]);
            strcat(buf, tmp);
        }
        buf[32] = '\0'; // 后面都是0，从32字节截断
        return string(buf);
    }

};

#endif //SCLI_DGST_H
